#import "SMRRatingCell.h"


#pragma mark Constants

const unichar SMRRatingCellHalfCode = 0x00BD;
//const CGFloat SMRRatingCellStarLength = 13.0f;
//const CGFloat SMRRatingCellHalfStarFontSize = 12.0f;
const CGFloat SMRRatingCellPlaceholderRadius = 2.0f;
NSString* const SMRRatingCellAllowsHalfStarsKey = @"SMRRatingCellAllowsHalfStarsKey";


#pragma mark -
#pragma mark Class Globals

static NSColor *SMRRatingCellStarColorDarkBackground;
static NSColor *SMRRatingCellStarColorLightBackground;
static NSBezierPath *SMRRatingCellStarPath;
static NSBezierPath *SMRRatingCellPlaceholderPath;


#pragma mark -
@interface SMRRatingCell (SMRRatingCellPrivateMethods)
-(NSAttributedString *)attributedStringWithStarCount_:(float)starCount;
+(NSBezierPath *)starPath_;
-(void)drawStarAtPoint_:(NSPoint)drawPoint;
-(void)drawPlaceholderAtPoint_:(NSPoint)drawPoint;
-(NSAttributedString *)halfStarString_;
@end


#pragma mark -
@implementation SMRRatingCell

@synthesize allowsHalfStars;
@synthesize ratingColor;


#pragma mark -
#pragma mark Initialization

+(void)initialize {
	SMRRatingCellStarPath = [[self starPath_] retain];
//	NSAffineTransform *scaleTransform = [NSAffineTransform transform];
//	[scaleTransform scaleBy:SMRRatingCellStarLength];
//	[SMRRatingCellStarPath transformUsingAffineTransform:scaleTransform];

	SMRRatingCellStarColorDarkBackground = [[NSColor colorWithCalibratedWhite:1.0f alpha:1.0f] retain];
	SMRRatingCellStarColorLightBackground = [[NSColor colorWithCalibratedWhite:0.0f alpha:1.0f] retain];

	NSRect placeholderRect = NSMakeRect(-SMRRatingCellPlaceholderRadius / 2, -SMRRatingCellPlaceholderRadius / 2, SMRRatingCellPlaceholderRadius, SMRRatingCellPlaceholderRadius);
	SMRRatingCellPlaceholderPath = [[NSBezierPath bezierPathWithOvalInRect:placeholderRect] retain];
}

-(id)initWithLevelIndicatorStyle:(NSLevelIndicatorStyle)levelIndicatorStyle {
	if (!(self = [super initWithLevelIndicatorStyle:levelIndicatorStyle]))
		return nil;
	[self setLevelIndicatorStyle:NSRatingLevelIndicatorStyle];
	[self setAllowsHalfStars:YES];
	return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder {
	if (!(self = [super initWithCoder:aDecoder]))
		return nil;
	[self setAllowsHalfStars:[aDecoder decodeBoolForKey:SMRRatingCellAllowsHalfStarsKey]];
	return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder {
	[super encodeWithCoder:aCoder];
	[aCoder encodeBool:[self allowsHalfStars] forKey:SMRRatingCellAllowsHalfStarsKey];
}


#pragma mark -
#pragma mark Layout

-(NSSize)cellSize {
	CGFloat controlViewHeight = [[self controlView] frame].size.height;
	return NSMakeSize(controlViewHeight * [self maxValue], controlViewHeight);
}

-(NSSize)cellSizeForBounds:(NSRect)aRect {
	NSSize minSize = [self cellSize];
	if (aRect.size.width < minSize.width)
		minSize.width = aRect.size.width;
	if (aRect.size.height < minSize.height)
		minSize.height = aRect.size.height;
	return minSize;
}


#pragma mark -
#pragma mark Drawing

-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
	BOOL shouldDrawLight = [self backgroundStyle] == NSBackgroundStyleLight;
	NSColor *fillColor = [self ratingColor];
	if (!fillColor)
		fillColor = shouldDrawLight ? SMRRatingCellStarColorLightBackground : SMRRatingCellStarColorDarkBackground;
	[fillColor setFill];
	double rating = [[self objectValue] doubleValue];
	CGFloat starLength = [controlView frame].size.height;
	for (NSUInteger currentPosition = 0; currentPosition < [self maxValue]; currentPosition++) {
		NSPoint drawingPoint = NSMakePoint(currentPosition * starLength, 0.0f);
		if (rating >= 1.0) 
			[self drawStarAtPoint_:drawingPoint];
		else if (rating > 0 && [self allowsHalfStars]) {
			NSAttributedString *halfStarString = [self halfStarString_];
			NSSize stringSize = [halfStarString size];
			NSRect drawingRect = NSMakeRect(drawingPoint.x, drawingPoint.y - 1, stringSize.width, stringSize.height);
			[halfStarString drawInRect:drawingRect];
		}
		else if ([self isHighlighted]) {
			NSPoint placeholderPoint = drawingPoint;
			placeholderPoint.x += starLength / 2;
			placeholderPoint.y += starLength / 2;
			[self drawPlaceholderAtPoint_:placeholderPoint];
		}
		rating -= 1.0f;
	}
}

-(void)drawStarAtPoint_:(NSPoint)drawPoint {
	CGFloat starLength = [[self controlView] frame].size.height;
	NSAffineTransform *scaleTransform = [NSAffineTransform transform];
	NSAffineTransform *translateTransform = [NSAffineTransform transform];
	[scaleTransform scaleBy:starLength];
	[translateTransform translateXBy:drawPoint.x yBy:drawPoint.y];

	NSBezierPath *starPath = [SMRRatingCellStarPath copy];
	[starPath transformUsingAffineTransform:scaleTransform];
	[starPath transformUsingAffineTransform:translateTransform];
	[starPath fill];
}

-(void)drawPlaceholderAtPoint_:(NSPoint)drawPoint {
	NSAffineTransform *translateTransform = [NSAffineTransform transform];
	[translateTransform translateXBy:drawPoint.x yBy:drawPoint.y];
	NSBezierPath *placeholderPath = [SMRRatingCellPlaceholderPath copy];
	[placeholderPath transformUsingAffineTransform:translateTransform];
	[placeholderPath fill];
}


#pragma mark -
#pragma mark Event handling

-(BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)flag {
	CGFloat starLength = [controlView frame].size.height;
	while ([theEvent type] != NSLeftMouseUp) {
		NSPoint mousePoint = [controlView convertPointFromBase:[theEvent locationInWindow]];
		float rawRating = (mousePoint.x  + (starLength / 2)) / starLength;
		float newRating = round(rawRating * 2) / 2;
		float currentRating = [[self objectValue] doubleValue];
		if (currentRating != newRating) {
			[self setDoubleValue:newRating];
			[controlView setNeedsDisplay:YES];
		}
		theEvent = [[controlView window] nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask)];
	}
	// Even if there's no action, this prompts the control into executing its value binding
	if ([controlView isKindOfClass:[NSControl class]]) {
		NSControl *control = (NSControl *)controlView;
		[control sendAction:[control action] to:[control target]];
	}

	return YES;
}


#pragma mark -
#pragma mark Private

+(NSBezierPath *)starPath_ {
	CGFloat radius = 0.5;
	NSUInteger numberOfSides = 5;
	CGFloat angleInRadians[numberOfSides];
	
	for (NSUInteger angleIndex = 0; angleIndex < numberOfSides; angleIndex++) {
		CGFloat degrees = (360.0 / numberOfSides * angleIndex) + 90.0f;
		angleInRadians[angleIndex] = degrees * M_PI / 180;
	}
	
	NSBezierPath *starPath = [NSBezierPath bezierPath];
	[starPath moveToPoint:NSMakePoint(radius + radius * cos(angleInRadians[0]), radius + radius * sin(angleInRadians[0]))];
	
	for (NSUInteger currentIndex = 1; currentIndex < numberOfSides; currentIndex++) {
		NSUInteger adjustedIndex = (currentIndex * 2) % numberOfSides;
		[starPath lineToPoint:NSMakePoint(radius + radius * cos(angleInRadians[adjustedIndex]), radius + radius * sin(angleInRadians[adjustedIndex]))];
	}
	[starPath closePath];
	return starPath;
}

-(NSAttributedString *)halfStarString_ {
	NSColor *textColor = [self ratingColor];
	if (!textColor) {
		BOOL shouldDrawLight = [self backgroundStyle] == NSBackgroundStyleLight;
		textColor = shouldDrawLight ? SMRRatingCellStarColorLightBackground : SMRRatingCellStarColorDarkBackground;
	}
	CGFloat starFontSize = [[self controlView] frame].size.height - 1;
	NSFont *starFont = [NSFont systemFontOfSize:starFontSize];
	NSDictionary *starAttributes = [NSDictionary dictionaryWithObjectsAndKeys:textColor, NSForegroundColorAttributeName, starFont, NSFontAttributeName, nil];
	NSString *halfStarString = [NSString stringWithCharacters:&SMRRatingCellHalfCode length:1];
	return [[[NSAttributedString alloc] initWithString:halfStarString attributes:starAttributes] autorelease];
}

@end
